﻿using System;
using System.IO;
using System.Net;
using System.Web;
using System.Web.Hosting;
using Microsoft.Win32.SafeHandles;

//自定义命名空间
using DreamCube.Foundation.Basic.Extensions.System;

namespace WebServer.Service.BLL
{
    [Serializable]
    public class WebRequest : HttpWorkerRequest
    {
        #region "字段"

        private HttpListenerContext context;

        private static Int32 bufferLength = 64 * 1024;

        /// <summary>
        /// GetUriPath()方法的结果值
        /// </summary>
        private String uriPath = null;

        #endregion

        #region "构造方法"

        public WebRequest(HttpListenerContext context)
        {
            this.context = context;
        }

        #endregion

        #region "实现抽象方法"

        /// <summary>
        /// 由运行时使用以通知 HttpWorkerRequest 当前请求的请求处理已完成。 
        /// </summary>
        public override void EndOfRequest()
        {
            this.context.Response.OutputStream.Close();
            this.context.Response.Close();
        }

        /// <summary>
        /// 将所有挂起的响应数据发送到客户端。
        /// </summary>
        /// <param name="finalFlush"></param>
        public override void FlushResponse(bool finalFlush)
        {
            this.context.Response.OutputStream.Flush();
            if (finalFlush) this.context.Response.OutputStream.Close();
        }

        /// <summary>
        /// 请求标头中返回的 HTTP 谓词。
        /// HTTP 方法通常是 GET 或 POST，具体取决于客户端所需的操作。
        /// </summary>
        /// <returns></returns>
        public override string GetHttpVerbName()
        {
            return this.context.Request.HttpMethod;
        }

        /// <summary>
        /// 提供对请求的 HTTP 版本（如“HTTP/1.1”）的访问。
        /// </summary>
        /// <returns></returns>
        public override string GetHttpVersion()
        {
            return String.Format("HTTP/{0}.{1}",
                                 this.context.Request.ProtocolVersion.Major,
                                 this.context.Request.ProtocolVersion.Minor);
        }

        /// <summary>
        /// 请求标头中返回的服务器 IP 地址。
        /// </summary>
        /// <returns></returns>
        public override string GetLocalAddress()
        {
            String ipAddress = this.context.Request.LocalEndPoint.Address.ToString();
            if (ipAddress == "::1")
                return "127.0.0.1";
            return ipAddress;
        }

        /// <summary>
        /// 请求标头中返回的服务器端口号。
        /// </summary>
        /// <returns></returns>
        public override int GetLocalPort()
        {
            return this.context.Request.LocalEndPoint.Port;
        }

        /// <summary>
        /// 请求查询字符串。
        /// </summary>
        /// <returns></returns>
        public override string GetQueryString()
        {
            String url = this.context.Request.RawUrl;
            return url.RightOfLast("?");
        }

        /// <summary>
        /// 返回附加了查询字符串的请求标头中包含的 URL 路径(不包括主机和端口号)
        /// </summary>
        /// <returns></returns>
        public override string GetRawUrl()
        {
            return this.context.Request.RawUrl;
        }

        /// <summary>
        /// 客户端的 IP 地址。
        /// </summary>
        /// <returns></returns>
        public override string GetRemoteAddress()
        {
            String ipAddress = this.context.Request.RemoteEndPoint.Address.ToString();
            if (ipAddress == "::1")
                return "127.0.0.1";
            return ipAddress;
        }

        /// <summary>
        /// 客户端的 HTTP 端口号。
        /// </summary>
        /// <returns></returns>
        public override int GetRemotePort()
        {
            return this.context.Request.RemoteEndPoint.Port;
        }

        /// <summary>
        /// 返回请求的 URI 的虚拟路径。
        /// 对于 URI file://computer/file.ext，绝对路径为 /file.ext 而本地路径为 \\computer\file.ext。 
        /// </summary>
        /// <returns></returns>
        public override string GetUriPath()
        {
            String url = this.context.Request.RawUrl.LeftOfLast("?"); //去掉查询字符串
            if(url.EndsWith("/")) url = url.Substring(0,url.Length -1);
            String virtualPath = url.RightOfLast("/");

            return virtualPath;
        }

        /// <summary>
        /// 将标准 HTTP 标头添加到响应。
        /// </summary>
        /// <param name="index">标头索引。例如 HeaderContentLength。 </param>
        /// <param name="value"></param>
        public override void SendKnownResponseHeader(int index, string value)
        {
            this.context.Response.Headers[HttpWorkerRequest.GetKnownResponseHeaderName(index)] = value;
        }

        /// <summary>
        /// 将指定文件的内容添加到响应并指定文件中的起始位置和要发送的字节数。
        /// </summary>
        /// <param name="handle">要发送的文件的句柄。</param>
        /// <param name="offset">文件中的起始位置。</param>
        /// <param name="length">要发送的字节数。</param>
        public override void SendResponseFromFile(IntPtr handle, long offset, long length)
        {
            if (length <= 0) return;
            using (var sfh = new SafeFileHandle(handle, false))
            {
                //对于那些没有 FileShare 参数的 FileStream 的构造函数， FileShare.Read 是默认值。 
                using (FileStream fs = new FileStream(sfh, FileAccess.Read))
                {
                    SendResponseFromFileStream(fs, offset, length);
                }
            }
        }

        /// <summary>
        /// 将指定文件的内容添加到响应并指定文件中的起始位置和要发送的字节数。
        /// </summary>
        /// <param name="filename">要发送的文件的名称。</param>
        /// <param name="offset">文件中的起始位置。</param>
        /// <param name="length">要发送的字节数。</param>
        public override void SendResponseFromFile(string filename, long offset, long length)
        {
            if (length <= 0) return;
            using (FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read))
            {
                SendResponseFromFileStream(fs, offset, length);
            }
        }

        /// <summary>
        /// 将字节数组中指定数目的字节添加到响应。
        /// </summary>
        /// <param name="data"></param>
        /// <param name="length"></param>
        public override void SendResponseFromMemory(byte[] data, int length)
        {
            this.context.Response.OutputStream.Write(data, 0, length);
        }

        /// <summary>
        /// 指定响应的 HTTP 状态代码和状态说明，例如 SendStatus(200, "Ok")。 
        /// </summary>
        /// <param name="statusCode"></param>
        /// <param name="statusDescription"></param>
        public override void SendStatus(int statusCode, string statusDescription)
        {
            this.context.Response.StatusCode = statusCode;
            this.context.Response.StatusDescription = statusDescription;
        }

        /// <summary>
        /// 将非标准 HTTP 标头添加到响应。
        /// </summary>
        /// <param name="name"></param>
        /// <param name="value"></param>
        public override void SendUnknownResponseHeader(string name, string value)
        {
            this.context.Response.Headers.Add(name, value);
        }

        #endregion

        #region "私有方法"

        /// <summary>
        /// 把文件流输出到输出流中
        /// </summary>
        /// <param name="f"></param>
        /// <param name="offset">输出流的偏移位置</param>
        /// <param name="length">输出流的长度，-1表示默认为输出整个流 fs 的数据</param>
        private void SendResponseFromFileStream(Stream fs, Int64 offset, Int64 length = -1)
        {
            //文件流的长度
            Int64 fileSize = fs.Length;
            if (length == -1) length = fileSize - offset;
            if (length == 0 || offset < 0 || length > fileSize - offset) return;
            if (offset > 0) fs.Seek(offset, SeekOrigin.Begin);
            if (length <= WebRequest.bufferLength)
            {
                var fileBytes = new Byte[(Int64)length];
                var bytesRead = fs.Read(fileBytes, 0, (Int32)length);
                SendResponseFromMemory(fileBytes, bytesRead);
            }
            else
            {
                var chunk = new Byte[WebRequest.bufferLength];
                var bytesRemaining = (Int32)length;

                while (bytesRemaining > 0)
                {
                    var bytesToRead = (bytesRemaining < WebRequest.bufferLength) ? bytesRemaining : WebRequest.bufferLength;
                    var bytesRead = fs.Read(chunk, 0, bytesToRead);

                    SendResponseFromMemory(chunk, bytesRead);
                    bytesRemaining -= bytesRead;

                    // 调用flush，以释放缓冲区的内存
                    if ((bytesRemaining > 0) && (bytesRead > 0))
                        FlushResponse(false);
                }
            }
        }

        #endregion
    }
}
